import { createView } from './view'
import { createController } from './controller'
import { archiver } from './archiver'
// import { cloneDeep } from 'lodash'
// const gm:GameModule = {}

/** 2048
 * @file        TwentyFortyEight.ts
 * @module      2048 游戏类
 * @version     0.0.1
 * @copyright   小猪熊工作室
 * @author      wulidoge
 * @email       wulidoge@qq.com
 * @created     2025-10-14
 * @modified    2025-10-14
 * @description 2048 小游戏主类：包含游戏核心逻辑、动画描述、DOM 视图、键盘输入控制，需结合 game.css 样式表
 */
export default class Game {
  // 策略模式：一维索引转二维索引，用于方块合并
  static cmap: IndexToCoordinate = {
    // 上下左右
    up: (i: number, SIZE: number) => [i % SIZE, Math.floor(i / SIZE) % SIZE],
    // 下上左右
    down: (i: number, SIZE: number) => [SIZE - 1 - (i % SIZE), Math.floor(i / SIZE) % SIZE],
    // 左右上下
    left: (i: number, SIZE: number) => [Math.floor(i / SIZE) % SIZE, i % SIZE],
    // 右左上下
    right: (i: number, SIZE: number) => [Math.floor(i / SIZE) % SIZE, SIZE - 1 - (i % SIZE)],
  }
  // DOM 容器（挂载游戏视图的根元素）
  private view: HTMLElement
  private _data: GameData = {
    size: 4,
    score: 0,
    grid: [
      [0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0],
    ],
    animations: [],
    time: 0,
    state: -1,
  }

  constructor(size: number = 4) {
    console.log('初始化游戏')
    if (size < 2 || size > 10) throw new Error('游戏尺寸错误')
    this._data.grid = Array.from({ length: size }, () => Array.from({ length: size }, () => 0))
    this._data.size = size
    this.createBlock()
    const { view, update } = createView(size)
    this.view = view
    this.update = update
    update(this.data)
  }
  // private initData():GameData {}
  private isInRange(i: number, j: number) {
    const SIZE = this.size
    return i >= 0 && i < SIZE && j >= 0 && j < SIZE
  }
  private setBlock(i: number, j: number, value: number) {
    console.log(`设置方块，位置：${i} ${j}；值：${value}`)
    if (!this.isInRange(i, j)) throw new Error('grid 索引越界')
    ;(this._data.grid[i] as number[])[j] = value
    return true
  }
  private getBlock(i: number, j: number) {
    if (!this.isInRange(i, j)) throw new Error('grid 索引越界')
    const value = this._data.grid[i]![j]!
    console.log(`获取方块：${i} ${j}；值：${value}`)
    return value
  }
  // 结束检测
  private isOver() {
    const SIZE = this.size
    // 判断是否有空格
    for (let i = 0; i < SIZE; i++) {
      for (let j = 0; j < SIZE; j++) {
        if (this.getBlock(i, j) == 0) {
          return false
        }
      }
    }
    // 判断相邻方块是否可合并 4 * SIZE * SIZE
    for (let i = 0; i < SIZE; i++) {
      for (let j = 0; j < SIZE; j++) {
        if (
          (j + 1 < SIZE && this.getBlock(i, j) == this.getBlock(i, j + 1)) ||
          (i + 1 < SIZE && this.getBlock(i, j) == this.getBlock(i + 1, j)) ||
          (i - 1 >= 0 && this.getBlock(i, j) == this.getBlock(i - 1, j)) ||
          (j - 1 >= 0 && this.getBlock(i, j) == this.getBlock(i, j - 1))
        )
          return false
      }
    }
    return true
  }
  // 创造方块：在网格的所有空格中随机创建一个方块（成功移动或合并后调用）
  private createBlock() {
    const value = Math.random() > 0.1 ? 2 : 4
    console.log('创建方块：', value)
    const SIZE = this.size
    // console.log(SIZE)
    // 获取所有空格
    const emptyBlocks: [number, number][] = []
    for (let i = 0; i < SIZE; i++) {
      for (let j = 0; j < SIZE; j++) {
        if (this.getBlock(i, j) == 0) {
          emptyBlocks.push([i, j])
        }
      }
    }
    // emptyBlocks.length -> [1, SIZE * SIZE]
    const [i, j] = emptyBlocks[Math.floor(Math.random() * emptyBlocks.length)]
    this.setBlock(i, j, value)
    this._data.animations.push({ type: 'create', start: [i, j], value })
  }
  /** 合并方块
   * @function mergeBlocks
   * @param {Direction} d - 方向
   * @returns {boolean} - 是否发生合并或移动
   * @description  随操作方向堆叠方块（相同则合并）
   */
  private mergeBlocks(d: Direction) {
    // console.log('尝试合并方块', d)
    const SIZE = this.size
    const N = SIZE * SIZE
    let [c, n] = [0, 1]
    let ci, cj, ni, nj, cv, nv
    let changed = false
    const setIndex = () => {
      n++
      if (n % SIZE == 0) {
        c = n
        n++
      }
    }
    while (n < N) {
      ;[ci, cj] = Game.cmap[d](c, SIZE)
      ;[ni, nj] = Game.cmap[d](n, SIZE)
      nv = this.getBlock(ni, nj)
      // 跳过空方块
      if (nv == 0) {
        setIndex()
        continue
      }
      cv = this.getBlock(ci, cj)
      // current 为 0 时，next 移动到 current
      if (cv == 0) {
        this.setBlock(ci, cj, nv)
        this.setBlock(ni, nj, 0)
        this._data.animations.push({
          type: 'move',
          start: [ni, nj],
          end: [ci, cj],
          value: nv,
        })
        changed = true
      }
      // current 不为 0 且与 next 相等时，合并
      else if (nv == cv) {
        c++
        cv += nv
        this.setBlock(ci, cj, cv)
        this.setBlock(ni, nj, 0)
        this._data.score += cv
        this._data.animations.push({
          type: 'merge',
          start: [ni, nj],
          end: [ci, cj],
          value: nv,
        })
        changed = true
      }
      // current 不为 0 且与 next 不相等且距离相差超过 1 时，移动 next 到 current 上方
      else if (n > c + 1) {
        c++
        ;[ci, cj] = Game.cmap[d](c, SIZE)
        this.setBlock(ci, cj, nv)
        this.setBlock(ni, nj, 0)
        this._data.animations.push({
          type: 'move',
          start: [ni, nj],
          end: [ci, cj],
          value: nv,
        })
        changed = true
      }
      // current 不为 0 且与 next 不相等且两者相邻
      else c++
      setIndex()
    }
    return changed
  }
  get size() {
    return this._data.size
  }
  // 获取游戏数据深拷贝
  get data() {
    return structuredClone(this._data)
  }
  update: (data: GameData) => void
  // 挂载游戏（视图、控制器）
  mount(selector: string) {
    console.log('挂载游戏')
    const container = document.querySelector(selector)
    if (!container) throw new Error('未找到元素')
    container.appendChild(this.view)
    // 游戏控制器
    createController(this)
  }
  // 滑动方块
  slide(d: Direction) {
    console.log('滑动方块', d)
    // 发生移动或合并
    if (this.mergeBlocks(d)) this.createBlock()
    // 检测游戏是否结束
    if (this.isOver()) {
      this._data.state = 2
      console.log('游戏结束')
    }
    // 执行并清空动画栈
    this.update(this.data)
  }
  // 游戏开关
  switch() {
    this._data.state = this._data.state ? 0 : 1
  }
  // 重玩游戏
  restart() {}
  // 保存游戏
  save() {}
  // 加载游戏（存档）
  load() {}
}
